package controller;

import java.util.ArrayList;
import java.util.regex.Pattern;

import dal.datastore.TableData;
import dto.ConditionStatementDTO;
import dto.RowDTO;

/**
 * @author Stanley
 * 
 */
public class WHERECommand {
	private String _command;
	private TableData _data;

	public WHERECommand(String _command, TableData _data) {
		super();
		this._command = _command;
		this._data = _data;
	}

	public WHERECommand(String _command) {
		super();
		this._command = _command;
	}

	/**
	 * Execute where command
	 * 
	 * @param indexes
	 * @return
	 * @throws Exception
	 */
	public TableData execute(ArrayList<Integer> indexes) throws Exception {
		if (isWhereCommand()) {
			ArrayList<ConditionStatementDTO> conditionStatements = new ArrayList<>();
			ArrayList<String> and_or = new ArrayList<>();
			parse(conditionStatements, and_or, _command);

			if (indexes == null) {
				indexes = new ArrayList<Integer>();
			}
			return _data.selectDataWithConditions(and_or, conditionStatements, indexes);
		} else
			return null;
	}

	private boolean isWhereCommand() {
		String operators = "(=|!=|>|<|<=|>=|like|not like)";
		String and = "(\\s+(?i)and\\s+((\\w+|(-)?\\d+)\\s*" + operators
				+ "\\s*((\\'(\\%)*\\w+(\\s*\\w+\\s*)*(\\%)*\\'|(-)?\\d+))))*";
		String or = "(\\s+(?i)or\\s+((\\w+|(-)?\\d+)\\s*" + operators
				+ "\\s*((\\'(\\%)*\\w+(\\s*\\w+\\s*)*(\\%)*\\'|(-)?\\d+))))*";
		String where = "((\\s*((\\w+|(-)?\\d+)\\s*" + operators
				+ "\\s*((\\'(\\%)*\\w+(\\s*\\w+\\s*)*(\\%)*\\'|(-)?\\d+))))*" + and
				+ or + ")*";

		return _command.matches(where);
	}

	/**
	 * Parse where command.
	 * 
	 * @param conditionStatements
	 *            references to an array list.
	 * @param command
	 *            where string command
	 * @throws Exception
	 */
	private void parse(ArrayList<ConditionStatementDTO> conditionStatements,
			ArrayList<String> and_or, String command) throws Exception {
		String temp = command.toLowerCase();
		Pattern p = Pattern.compile("and", Pattern.CASE_INSENSITIVE);
		/**
		 * If _command contains AND operator
		 * */
		if (temp.contains("and")) {
			and_or.add("and");
			p = Pattern.compile("and", Pattern.CASE_INSENSITIVE);
			ArrayList<String> strings = QueryParser.removeAllEmptyString(p
					.split(command));
			String leftAND = strings.get(0);
			String rightAND = strings.get(1);

			parse(conditionStatements, and_or, leftAND);
			parse(conditionStatements, and_or, rightAND);
		} else if (temp.contains("or")) {
			and_or.add("or");
			p = Pattern.compile("or", Pattern.CASE_INSENSITIVE);
			ArrayList<String> strings = QueryParser.removeAllEmptyString(p
					.split(command));
			String leftOR = strings.get(0);
			String rightOR = strings.get(1);

			parse(conditionStatements, and_or, leftOR);
			parse(conditionStatements, and_or, rightOR);
		} else
			conditionStatements.add(parseAtomConditionStatement(command));
	}

	/**
	 * Parse atom condition statement Example a=1, a like 'a', a < 4, a > 2,
	 * etc.
	 * 
	 * @param statement
	 * @return ConditionStatementDTO
	 * @throws Exception
	 */
	private ConditionStatementDTO parseAtomConditionStatement(String statement)
			throws Exception {
		try {

			ArrayList<String> strings = null;

			if (statement.contains(">=")) {
				Pattern p = Pattern.compile("(>=|')", Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.GREATER_THAN_OR_EQUALS);
			}
			if (statement.contains(">")) {
				Pattern p = Pattern.compile("(>|')", Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.GREATER_THAN);
			}
			if (statement.contains("<=")) {
				Pattern p = Pattern.compile("(<=|')", Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.LESS_THAN_OR_EQUALS);
			}
			if (statement.contains("<")) {
				Pattern p = Pattern.compile("(<|')", Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.LESS_THAN);
			}
			if (statement.contains("not like")) {
				Pattern p = Pattern.compile("(not like|')",
						Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.NOT_LIKE);
			}
			if (statement.contains("like")) {
				Pattern p = Pattern.compile("(like|')",
						Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.LIKE);
			}
			if (statement.contains("!=")) {
				Pattern p = Pattern.compile("(!=|')", Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.NOT_LIKE);
			}
			if (statement.contains("=")) {
				Pattern p = Pattern.compile("(=|')", Pattern.CASE_INSENSITIVE);
				strings = QueryParser.removeAllEmptyString(p.split(statement));
				String leftOperand = strings.get(0);
				String rightOperand = strings.get(1);
				return new ConditionStatementDTO(leftOperand, rightOperand,
						EOperatorType.EQUALS);
			}

		} catch (Exception e) {
			throw e;
		}
		return null;
	}

	public String getCommand() {
		return _command;
	}

	public void setCommand(String _command) {
		this._command = _command;
	}

}
